
#include <string.h>
#include <stdio.h>
#include "wrk_logger.h"
#include <uuid/uuid.h>
#include "include/aprintf.h"
#include <iostream>
#include <sstream>
#include <stdarg.h>
// https://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_demangling.html

using namespace wrk;

WrkLogger::WrkLogger()
{

}

WrkLogger::~WrkLogger()
{
}



pthread_mutex_t WrkLogger::log_mutex;
pthread_mutex_t WrkLogger::buffer_mutex;
WrkLogger *WrkLogger::logger = NULL;
std::string WrkLogger::log_file;
std::string WrkLogger::ori_log_file;
wrk_log_level_t WrkLogger::LOG_LEVEL = wrk_log_level_t::ERROR;  


std::string WrkLogger::log_backtrace()
{
    std::stringstream ss;
    int size = 0;
    void *array[20];
    char **strings = NULL;
    int i = 0;										

    size = backtrace(array, 20);
    strings = backtrace_symbols(array, size);		
    if (strings == NULL)							
    {
        ss << "backtrace_symbols error: " <<strerror(errno);
        return ss.str();
    }												

    ss <<"Obtained "<< size << " stack frames." <<std::endl;

    for (i = 0; i < size; i++)						
    {						
        ss <<"[PID:"<<getpid()<<"]  "<< strings[i] <<std::endl;										
    }												

    free(strings);									
    strings = NULL;	

    return ss.str();
}

WrkLogger *WrkLogger::get_instance()
{
    if(logger==NULL) {
        if(pthread_mutex_init(&log_mutex, NULL) !=0) throw "pthread_mutex_init log_mutex failure!";

        if(pthread_mutex_init(&buffer_mutex, NULL) !=0) throw "pthread_mutex_init buffer_mutex failure!";

        pthread_mutex_lock(&log_mutex);

        if(logger==NULL) {
            logger = new WrkLogger();
        }

        pthread_mutex_unlock(&log_mutex);
    }

    return logger;
}

void WrkLogger::init_logger(const std::string &logfile)
{
    WrkLogger::log_file = logfile;
    WrkLogger::ori_log_file = log_file;
}

std::string WrkLogger::format(const char *fmt, ...)
{
    std::string str;
    va_list args, pargs;
    va_start(args, fmt);
    va_copy(pargs, args); 
    {
        int nLength = vsnprintf(NULL, 0, fmt, pargs);
        nLength += 1;  //上面返回的长度是包含\0，这里加上
        char *buf = (char *)malloc(nLength);
        vsprintf(buf, fmt, args);
        str.assign(buf);
        free(buf);
    }

    va_end(args);
    return str;
}
void WrkLogger::set_log_level(wrk_log_level_t level)
{
    LOG_LEVEL = level;
}

std::string WrkLogger::get_uuid()
{
    uuid_t uu;
    char buf[64];
    uuid_generate(uu);
    uuid_unparse_lower(uu,buf);
    
    return std::string(buf);
}

void WrkLogger::log_message(wrk_log_level_t cur_level, const char *filename, const char *func, uint32_t line, const std::string &msg)
{
    if(cur_level>WrkLogger::LOG_LEVEL) return;
    
    pthread_mutex_lock(&buffer_mutex);
    std::string level;
    switch (cur_level)
    {
    case wrk_log_level_t::ERROR :
        /* code */
        level = "ERROR";
        break;
    case wrk_log_level_t::WARNING :
        /* code */
        level = "WARNING";
        break;
    case wrk_log_level_t::INFO :
        /* code */
        level = "INFO";
        break;
    case wrk_log_level_t::DEBUG :
        /* code */
        level = "DEBUG";
        break;
    
    default:
        break;
    }	
    
    std::ofstream file;
    if(WrkLogger::log_file != std::string(ori_log_file + "." + today())) {
        WrkLogger::log_file = std::string(ori_log_file + "." + today());
    }
																							
	if(file.is_open() == false) 
        file.open(log_file.c_str(),std::ios::app|std::ios::out);
    pthread_t tid = pthread_self();
    file <<"["<<NOW()<<"]["<< level <<"][PID:"<<getpid()<<"][TID:"
        << tid <<"]["<<func<<"@"<<filename<<":"<<line<<"] "<<msg <<std::endl;

    if(cur_level==wrk_log_level_t::ERROR) file << log_backtrace() <<std::endl;
    pthread_mutex_unlock(&buffer_mutex);
}


void WrkLogger::test(std::string msg, const char *filename, const char *func, uint32_t line)
{
    std::cout << WrkLogger::format("[%s][%s][PID:%d][%s@%s:%d] ", NOW(), "INFO", getpid(), func, filename, line) << msg <<std::endl; 
}
